﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using CuteRibs.WowArmory.Models;
using CuteRibs.WowArmory.ViewModels;
using ICSharpCode.SharpZipLib.GZip;
using Newtonsoft.Json;

namespace CuteRibs.WowArmory
{
	public class WowClient
	{
		private const string comma = ",";

		#region Blizzard community platform api

		#region query

		private static readonly string realmStatusUrl = "http://{0}/api/wow/realm/status?locale={1}&realms={2}";
		private static readonly string characterUrl = "http://{0}/api/wow/character/{1}/{2}?locale={3}&fields={3}";
		private static readonly string guildUrl = "http://{0}/api/wow/guild/{1}/{2}?locale={3}&fields={4}";
		private static readonly string itemUrl = "http://{0}/api/wow/item/{1}?locale={2}";
		private static readonly string itemSetUrl = "http://{0}/api/wow/item/set/{1}?locale={2}";
		private static readonly string achievementUrl = "http://{0}/api/wow/achievement/{1}?locale={2}";
		private static readonly string recipeUrl = "http://{0}/api/wow/recipe/{1}?locale={2}";
		private static readonly string arenaTeamUrl = "http://{0}/api/wow/arena/{1}/{2}/{3}?locale={4}";
		private static readonly string arenaLadderUrl = "http://{0}/api/wow/pvp/arena/{1}/{2}?locale={3}&page={4}&size={5}&asc={6}";
		private static readonly string ratedBattlegroundLadderUrl = "http://{0}/api/wow/pvp/ratedbg/ladder?locale={3}&page={4}&size={5}&asc={6}";

		private static readonly string battlegroupsUrl = "http://{0}/api/wow/data/battlegroups/?locale={1}";
		private static readonly string characterAchievementsUrl = "http://{0}/api/wow/data/character/achievements?locale={1}";
		private static readonly string characterRacesUrl = "http://{0}/api/wow/data/character/races?locale={1}";
		private static readonly string characterClassesUrl = "http://{0}/api/wow/data/character/classes?locale={1}";
		private static readonly string guildRewardResourceUrl = "http://{0}/api/wow/data/guild/rewards?locale={1}";
		private static readonly string guildPerkResourceUrl = "http://{0}/api/wow/data/guild/perks?locale={1}";
		private static readonly string itemClassResourceUrl = "http://{0}/api/wow/data/item/classes?locale={1}";

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="region">cn.battle.net</param>
		/// <returns></returns>
		public static async Task<RaceResource> GetRaceResource(Region region)
		{
			var url = string.Format(characterRacesUrl, region.Domain, region.GetLocale());
			var json = await DownloadString(url);
			var raceResource = JsonConvert.DeserializeObject<RaceResource>(json);

			if (raceResource != null && raceResource.ErrorStatus == null)
			{
				return raceResource;
			}

			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="region">cn.battle.net</param>
		/// <returns></returns>
		public static async Task<ClassResource> GetClassResource(Region region)
		{
			var url = string.Format(characterClassesUrl, region.Domain, region.GetLocale());
			var json = await DownloadString(url);
			var classResource = JsonConvert.DeserializeObject<ClassResource>(json);

			if (classResource != null && classResource.ErrorStatus == null)
			{
				return classResource;
			}

			return null;
		}

		/// <summary>
		/// GetRealmStatus
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="realmNames">回音山,echo-ridge,Arygos</param>
		/// <returns></returns>
		public static async Task<Realm[]> GetRealmStatus(Region host, params string[] realmNames)
		{
			var realms = realmNames == null ? null : string.Join(comma, realmNames);
			var url = string.Format(realmStatusUrl, host.Domain, host.GetLocale(), realms);
			var json = await DownloadString(url);
			var realmStatus = JsonConvert.DeserializeObject<RealmStatus>(json);

			if (realmStatus != null && realmStatus.ErrorStatus == null)
			{
				return realmStatus.Realms;
			}

			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="realmName">Arygos</param>
		/// <param charName="charName">Hilda</param>
		/// <param charName="fieldNames">achievements,appearance,feed,guild,hunterPets,items,mounts,pets,petSlots,professions,progression,pvp,quests,reputation,stats,talents,titles</param>
		/// <returns></returns>
		public static async Task<CharacterDetail> GetCharacter(Region host, string realmName, string charName, params string[] fieldNames)
		{
			var fields = fieldNames == null ? null : string.Join(comma, fieldNames);
			var url = string.Format(characterUrl, host.Domain, realmName, charName, host.GetLocale(), fields);
			var json = await DownloadString(url);
			var character = JsonConvert.DeserializeObject<CharacterDetail>(json);

			if (character != null && character.ErrorStatus == null)
			{
				return character;
			}

			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="realmName">Arygos</param>
		/// <param charName="guildName">Apostle</param>
		/// <param charName="fieldNames">achievements,members,news</param>
		/// <returns></returns>
		public static async Task<GuildDetails> GetGuild(Region host, string realmName, string guildName, params string[] fieldNames)
		{
			var fields = fieldNames == null ? null : string.Join(comma, fieldNames);
			var url = string.Format(guildUrl, host.Domain, realmName, guildName, host.GetLocale(), fields);
			var json = await DownloadString(url);
			var guild = JsonConvert.DeserializeObject<GuildDetails>(json);

			if (guild != null && guild.ErrorStatus == null)
			{
				return guild;
			}

			return null;
		}

		/// <summary>
		/// GetItem
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="itemId">78785</param>
		/// <returns></returns>
		public static async Task<ItemDetails> GetItem(Region host, int itemId)
		{
			var url = string.Format(itemUrl, host.Domain, itemId, host.GetLocale());
			var json = await DownloadString(url);
			var item = JsonConvert.DeserializeObject<ItemDetails>(json);

			if (item != null && item.ErrorStatus == null)
			{
				return item;
			}

			return null;
		}

		/// <summary>
		/// GetItemSet
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="itemId">1060</param>
		/// <returns></returns>
		public static async Task<ItemSet> GetItemSet(Region host, int itemSetId)
		{
			var url = string.Format(itemSetUrl, host.Domain, itemSetId, host.GetLocale());
			var json = await DownloadString(url);
			var itemSet = JsonConvert.DeserializeObject<ItemSet>(json);

			if (itemSet != null && itemSet.ErrorStatus == null)
			{
				return itemSet;
			}

			return null;
		}

		/// <summary>
		/// GetAchievement
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="itemId">1060</param>
		/// <returns></returns>
		public static async Task<Achievement> GetAchievement(Region host, int achievementId)
		{
			var url = string.Format(itemSetUrl, host.Domain, achievementId, host.GetLocale());
			var json = await DownloadString(url);
			var achievement = JsonConvert.DeserializeObject<Achievement>(json);

			if (achievement != null && achievement.ErrorStatus == null)
			{
				return achievement;
			}

			return null;
		}

		/// <summary>
		/// GetRecipe
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="itemId">1060</param>
		/// <returns></returns>
		public static async Task<Recipe> GetRecipe(Region host, int recipeId)
		{
			var url = string.Format(recipeUrl, host.Domain, recipeId, host.GetLocale());
			var json = await DownloadString(url);
			var recipe = JsonConvert.DeserializeObject<Recipe>(json);

			if (recipe != null && recipe.ErrorStatus == null)
			{
				return recipe;
			}

			return null;
		}

		/// <summary>
		/// GetArenaTeam
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="battlegroup">Battle Group 16</param>
		/// <param charName="teamSize">"2v2" | "3v3" | "5v5"</param>
		/// <param charName="teamName">牛肉来了</param>
		/// <returns></returns>
		public static async Task<ArenaTeam> GetArenaTeam(Region host, string battlegroup, string teamSize, string teamName)
		{
			var url = string.Format(arenaTeamUrl, host.Domain, battlegroup, teamSize, teamName, host.GetLocale());
			var json = await DownloadString(url);
			var arenaTeam = JsonConvert.DeserializeObject<ArenaTeam>(json);

			if (arenaTeam != null && arenaTeam.ErrorStatus == null)
			{
				return arenaTeam;
			}

			return null;
		}

		/// <summary>
		/// GetArenaLadder
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="battlegroup">Battle Group 16</param>
		/// <param charName="teamSize">"2v2" | "3v3" | "5v5"</param>
		/// <param charName="page">1</param>
		/// <param charName="size">50</param>
		/// <param charName="asc">true</param>
		/// <returns></returns>
		public static async Task<ArenaTeam[]> GetArenaLadder(Region host, string battlegroup, string teamSize, int page = 1, int size = 50, bool asc = true)
		{
			var url = string.Format(arenaTeamUrl, host.Domain, battlegroup, teamSize, host.GetLocale(), page, size, asc.ToString().ToLower());
			var json = await DownloadString(url);
			var arenaLadder = JsonConvert.DeserializeObject<ArenaLadder>(json);

			if (arenaLadder != null && arenaLadder.ErrorStatus == null)
			{
				return arenaLadder.Arenateam;
			}

			return null;
		}

		/// <summary>
		/// GetRatedBattlegroundLadder
		/// </summary>
		/// <param charName="region">cn.battle.net</param>
		/// <param charName="page">1</param>
		/// <param charName="size">50</param>
		/// <param charName="asc">true</param>
		/// <returns></returns>
		public static async Task<RatedBattleground[]> GetRatedBattlegroundLadder(Region host, int page = 1, int size = 50, bool asc = true)
		{
			var url = string.Format(arenaTeamUrl, host.Domain, host.GetLocale(), page, size, asc.ToString().ToLower());
			var json = await DownloadString(url);
			var ratedBattlegroundLadder = JsonConvert.DeserializeObject<RatedBattlegroundLadder>(json);

			if (ratedBattlegroundLadder != null && ratedBattlegroundLadder.ErrorStatus == null)
			{
				return ratedBattlegroundLadder.BgRecord;
			}

			return null;
		}

		#endregion

		#region Blizzard website

		#region query

		private static readonly string searchUrl = "http://{0}/wow/{1}/search?q={2}";
		private static readonly string searchDetailUrl = "http://{0}/wow/{1}/search?q={2}&f={3}&page={4}";
		private static readonly string avatarUrl = "http://{0}/static-render/{1}/{2}?alt=/wow/static/images/2d/avatar/{3}-{4}.jpg";

		#endregion

		public static async Task<SearchResultViewModel> Search(Region region, string term)
		{
			if (string.IsNullOrWhiteSpace(term))
			{
				return null;
			}

			var url = string.Format(searchUrl, region.Domain, region.GetLocale(), Uri.EscapeDataString(term));
			var xhtml = await DownloadString(url);
			return new SearchResultViewModel(xhtml, term);
		}

		public static async Task<T> Search<T>(Region region, string term, string field = null, int page = 1)
			where T : SearchResultViewModel
		{
			if (string.IsNullOrWhiteSpace(term))
			{
				return null;
			}

			var url = string.Format(searchDetailUrl, region.Domain, region.GetLocale(), Uri.EscapeDataString(term), field, page);
			var xhtml = await DownloadString(url);
			return Activator.CreateInstance(typeof(T), xhtml, term, page) as T;
		}

		/// <summary>
		/// http://us.battle.net/static-render/us/darkspear/85/78776405-avatar.jpg?alt=/wow/static/images/2d/avatar/2-0.jpg
		/// </summary>
		/// <param name="region"></param>
		/// <param name="character"></param>
		/// <returns></returns>
		public static string GetAvatar(Region region, Character character)
		{
			return string.Format(avatarUrl, region.Domain, region.Host, character.Thumbnail, character.Race, character.Gender);
		}

		#endregion

		public static async Task<string> DownloadString(string url)
		{
			string content = null;
			Stream stream;
			var client = new WebClient { Encoding = Encoding.UTF8 };
			client.Headers[HttpRequestHeader.AcceptEncoding] = "gzip";
			var input = await client.OpenReadTaskAsync(url);
			var contentEncoding = client.ResponseHeaders[HttpRequestHeader.ContentEncoding];

			if (!string.IsNullOrWhiteSpace(contentEncoding) && contentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
			{
				stream = new GZipInputStream(input);
			}
			else
			{
				stream = input;
			}

			if (stream != null)
			{
				using (StreamReader reader = new StreamReader(stream))
				{
					content = reader.ReadToEnd();
				}

				stream.Close();
			}

			return content;
		}
	}

	/// <summary>
	/// achievements,appearance,feed,guild,hunterPets,items,mounts,pets,petSlots,professions,progression,pvp,quests,reputation,stats,talents,titles
	/// </summary>
	public static class CharacterField
	{
		public static readonly string Achievements = "achievements";
		public static readonly string Appearance = "appearance";
		public static readonly string Feed = "feed";
		public static readonly string Guild = "guild";
		public static readonly string HunterPets = "hunterPets";
		public static readonly string Items = "items";
		public static readonly string Mounts = "mounts";
		public static readonly string Pets = "pets";
		public static readonly string PetSlots = "petSlots";
		public static readonly string Professions = "professions";
		public static readonly string Progression = "progression";
		public static readonly string Pvp = "pvp";
		public static readonly string Reputation = "reputation";
		public static readonly string Stats = "stats";
		public static readonly string Talents = "talents";
		public static readonly string Titles = "titles";
		public static readonly string Quests = "quests";
	}

	public static class GuildField
	{
		public static readonly string Achievements = "achievements";
		public static readonly string Members = "members";
		public static readonly string News = "news";
	}

	public static class SearchField
	{
		public const string Character = "wowcharacter";
		public const string Guild = "wowguild";
		public const string Item = "wowitem";
		public const string ArenaTeam = "wowarenateam";
		public const string Content = "static";
		public const string Forum = "post";
		public const string Blog = "article";
		public const string Support = "support";
	}
}
